package shell

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"pwsh-go/internal/algorithms"
	"pwsh-go/internal/builtin"
	"pwsh-go/internal/cache"
	"pwsh-go/internal/powershell"

	"golang.org/x/sys/windows"
)

type Command interface {
	Name() string
	Usage() string
	Run(args []string) error
}

var builtIns = map[string]Command{}

func init() {
	builtIns["ai"] = &builtin.AiCmd{}
}

// Shell represents the main REPL shell
type Shell struct {
	backend      powershell.Backend
	reader       *bufio.Reader
	cache        *cache.CommandCache
	optimizer    *algorithms.OptimizationManager
	sigChan      chan os.Signal
	currentCmd   *exec.Cmd
}

// New creates a new Shell instance
func New() (*Shell, error) {
	backend, err := powershell.NewBackend()
	if err != nil {
		return nil, fmt.Errorf("failed to create PowerShell backend: %w", err)
	}

	// 创建并启动优化管理器
	optimizer := algorithms.NewOptimizationManager()
	if err := optimizer.Start(); err != nil {
		return nil, fmt.Errorf("failed to start optimization manager: %w", err)
	}

	// 创建信号通道
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)

	shell := &Shell{
		backend:   backend,
		reader:    bufio.NewReader(os.Stdin),
		cache:     cache.NewCommandCache(),
		optimizer: optimizer,
		sigChan:   sigChan,
	}

	// 启动信号处理goroutine
	go shell.handleSignals()

	return shell, nil
}

// handleSignals handles OS signals in a separate goroutine
func (s *Shell) handleSignals() {
	for sig := range s.sigChan {
		switch sig {
		case os.Interrupt:
			// Ctrl+C received - terminate current command if running
			if s.currentCmd != nil && s.currentCmd.Process != nil {
				// Try to terminate the process gracefully
				if err := s.currentCmd.Process.Kill(); err != nil {
					fmt.Printf("\nWarning: failed to kill process: %v\n", err)
				}
				s.currentCmd = nil
			}
			// Do NOT exit the main program - just print a newline for formatting
			fmt.Println()
		case syscall.SIGTERM:
			// Graceful shutdown
			fmt.Println("\nReceived SIGTERM, shutting down...")
			os.Exit(0)
		}
	}
}

// Run starts the REPL loop
func Run() error {
	shell, err := New()
	if err != nil {
		return err
	}
	defer shell.Close()

	fmt.Println("PowerShell Go CLI - Type 'exit' to quit")

	for {
		fmt.Print("PS> ")

		line, err := shell.reader.ReadString('\n')
		if err != nil {
			if err.Error() == "EOF" {
				// EOF might be caused by Ctrl+C interrupting input
				// Reset the reader and continue instead of exiting
				shell.reader = bufio.NewReader(os.Stdin)
				fmt.Println() // Print newline for better formatting
				continue
			}
			return fmt.Errorf("failed to read input: %w", err)
		}

		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		parts := strings.Fields(line)
		if len(parts) == 0 {
			continue
		}
		cmdName := parts[0]
		args := parts[1:]

		if cmdName == "exit" {
			fmt.Println("Goodbye!")
			break
		}

		if c, ok := builtIns[cmdName]; ok {
			err := c.Run(args)
			if err != nil {
				fmt.Printf("Error: %v\n", err)
			}
			continue
		}

		command := line
		// Check if command is cacheable
		cacheableCommands := map[string]bool{
			"ipconfig":   true,
			"ls":         true,
			"git status": true,
			"pwd":        true,
			// Add more as needed
		}
		if cacheableCommands[command] {
			key := command + fmt.Sprintf("_%d", time.Now().Unix())
			if output, found := shell.cache.Get(key); found {
				fmt.Println(output)
				continue
			}
			result, err := shell.backend.Execute(command)
			if err != nil {
				fmt.Printf("Error: %v\n", err)
			} else {
				fmt.Println(result)
				shell.cache.Set(key, result)
			}
			continue
		}

		// For non-cacheable commands, use exec.Command for streaming output
		if err := shell.executeCommand(command); err != nil {
			fmt.Printf("Error: %v\n", err)
		}
	}

	return nil
}

// executeCommand executes a PowerShell command
func (s *Shell) executeCommand(command string) error {
	// Parse command to get the actual executable
	parts := strings.Fields(command)
	if len(parts) == 0 {
		return fmt.Errorf("empty command")
	}
	
	// For direct commands like curl, git, etc., execute them directly
	// For PowerShell-specific commands, use powershell wrapper
	directCommands := map[string]bool{
		"curl": true,
		"git":  true,
		"ping": true,
		"wget": true,
	}
	
	var cmd *exec.Cmd
	if directCommands[parts[0]] {
		// Execute command directly
		cmd = exec.Command(parts[0], parts[1:]...)
	} else {
		// Use PowerShell for other commands
		cmd = exec.Command("powershell", "-Command", command)
	}
	
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	// Create new process group so Ctrl+C only affects the child process
	cmd.SysProcAttr = &windows.SysProcAttr{
		CreationFlags: windows.CREATE_NEW_PROCESS_GROUP,
	}

	// Set current command for signal handling
	s.currentCmd = cmd
	defer func() {
		s.currentCmd = nil
	}()

	return cmd.Run()
}

// Close closes the shell and its backend
func (s *Shell) Close() error {
	// 停止优化管理器
	if s.optimizer != nil {
		if err := s.optimizer.Stop(); err != nil {
			fmt.Printf("Warning: failed to stop optimization manager: %v\n", err)
		}
	}
	return s.backend.Close()
}
